#    Copyright 2008, 2009 Tri-County Electric Cooperative, St. Matthews, South Carolina
#    This file is part of COOPGIS.
#    COOPGIS is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#    COOPGIS is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#    You should have received a copy of the GNU General Public License
#    along with COOPGIS.  If not, see <http://www.gnu.org/licenses/>.


#GetGPSPointLinuxFTPEdition.py attempts to get a NMEA GPS Reading 
#It then uploads the result to the FTPServer (presumably a virtual machine running on the host computer) about every 4 seconds
#The format is CSV with the following fields:
#Longitude [float decimal degrees], Latitude [float decimal degrees], Quality-Description [string]
import serial, time, locale, csv, ftplib 
locale.setlocale(locale.LC_ALL, "")
LocalFileName="/home/robyg/Desktop/Locations.csv"
FTPRemoteFileName="/incomingftpallpermissions/Locations.csv"
FTPUserName="anonymous"
FTPPassword="anonymous"
FTPServer="192.168.206.10"

locale.setlocale(locale.LC_ALL, "")

class GPS:
    def __init__(self):
        self.NumberOfSatellites=0
        self.DOP="N/A"
        self.AccuracyString=''
        self.Line=''
        self.SplitLine=[]
        self.Longitude=0
        self.Latitude=0
        self.GPGGAAttempts=15
        self.CorrectedIndicator=False
        self.Counter=0
        self.FileName="/home/robyg/Desktop/Locations.csv"
    def ParseGPGGA(self):
        """Example line from Trimble GPS:
                      0        1          2      3  4         5 6 7  8   9    10   11 12 13   14
        CorrectedLine  ='$GPGGA,133816.000,3337.8732,N,08046.4481,W,2,09,1.7,99.3,M,-32.2,M,,0000*5B'  """
        self.Line=self.Line.strip()
        self.SplitLine=self.Line.split(',')
        self.Latitude=float(self.SplitLine[2][:2]) + (float(self.SplitLine[2][2:4]) / 60.0) + float(self.SplitLine[2][4:])/60.0
        if(self.SplitLine[3] == 'S'):  #For Southern Hemisphere
            self.Latitude = -self.Latitude
        self.Longitude=float(self.SplitLine[4][:3]) + (float(self.SplitLine[4][3:5]) / 60.0) + float(self.SplitLine[4][5:])/60.0
        if(self.SplitLine[5] == 'W'): #For Western Hemisphere
            self.Longitude = -self.Longitude
        self.CorrectedIndicator = bool(int(self.SplitLine[6]) > 1)
        self.NumberOfSatellites = self.SplitLine[7]
        self.DOP = self.SplitLine[8]
        self.DOP = str(float(self.DOP))
    def GetCoordinate(self, cOMPortConnection):
        i = 0
        while(True):
            self.Line=cOMPortConnection.GetLine()  #This is the production setting
            #self.Line=cOMPortConnection.GetCorrectedTestLine()
            if(self.Line.startswith('$GPGGA')):
                break
            if(not self.Line):
                break #If not getting a line at all, then break rather then timing out multiple times
            i = i + 1
            if(i > self.GPGGAAttempts):
                return  "No Lat/Long:  " + self.Line
        try:
            self.ParseGPGGA()
        except:
            return "No Lat/Long:  " + self.Line
        return self.Line
    def SetAccuracyString(self):
        try:
           NumberOfSatellites=int(self.NumberOfSatellites)
           DilutionOfPrecision=float(self.DOP)
           if(self.CorrectedIndicator):
               CorrectedMultiplier = 0.75
           else:
               CorrectedMultiplier = 1
           BadAccuracyMeasure = CorrectedMultiplier * (2 *(12 - NumberOfSatellites) + (5 * (DilutionOfPrecision - 1)))
           if(NumberOfSatellites < 4):   #4 Satellite minimum, otherwise the reading is faulty or very inaccurate
               self.AccuracyString="No Reading"
               self.Latitude=0.0
               self.Longitude=0.0
           elif(BadAccuracyMeasure > 40):
               self.AccuracyString='Very Poor'
           elif(BadAccuracyMeasure > 20):
               self.AccuracyString='Poor'
           elif(BadAccuracyMeasure > 10):
               self.AccuracyString = 'Marginal'
           elif(BadAccuracyMeasure > 0): 
               self.AccuracyString = 'Good'
           else: 
               self.AccuracyString = 'Exceptional, let me know if it is wrong'
           if(self.CorrectedIndicator) and (NumberOfSatellites > 3):
               self.AccuracyString='WAAS:  ' + self.AccuracyString
           else:
               self.AccuracyString='Uncorrected:  ' + self.AccuracyString
        except ValueError:
            self.AccuracyString="No Reading"
            self.Latitude=0.0
            self.Longitude=0.0
    def Save(self, ConnectionIndicator):
        if(not ConnectionIndicator):
            self.AccuracyString="No NMEA"
        else:
            self.SetAccuracyString()
        File=open(self.FileName, "w")
        CSV=csv.writer(File)
        Row=[]
        Row.append(self.Longitude)
        Row.append(self.Latitude)
        Row.append(self.AccuracyString)
        print Row
        CSV.writerows([Row])
        File.close()
        
class COMPort:
    def __init__(self):
        self.COMPort=0
        self.baudrate=9600
        self.timeout=5
        self.OpenPort=None
        self.ConnectedToGPSIndicator=False
    def Connect(self):
        try:
            self.OpenPort.close()
        except:
            pass        
        Now=time.time()
        Future=time.time()
        try:
            self.OpenPort=serial.Serial(port='/dev/rfcomm' + str(self.COMPort), baudrate=self.baudrate, timeout=self.timeout)
            self.OpenPort.open()
            while(True):
                Line=self.GetLine()
                if(Line.startswith('$GPGGA')):
                    self.ConnectedToGPSIndicator=True
                    return "Connected to GPS on Com Port " + str(self.COMPort + 1) + " at " + time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime())
                else:
                    print "Line did not contain GPGGA: " + Line 
                if(not self.OpenPort.readline()):
                    self.OpenPort.close()
                    self.OpenPort.open()
                Future=time.time()
                if(not(Future-Now < 6)):
                    return "Not Connected to GPS but Connected To COM Port " + str(self.COMPort + 1) + " at " + time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime())
        except:
            return "Could Not Initialize the COM Port " + str(self.COMPort + 1)
    def GetLine(self):
        try:
            Line=self.OpenPort.readline()
            Line=Line.strip()
            print "Raw Line:  " + str(Line)
            return Line
        except :
            return ''
    def GetCorrectedTestLine(self):
        return '$GPGGA,133816.000,3337.8732,N,08046.4481,W,2,09,1.7,99.3,M,-32.2,M,,0000*5B'
    def GetUncorrectedTestLine(self):
        return '$GPGGA,194222.000,3337.8851,N,08046.4598,W,1,09,1.7,99.3,M,-32.2,M,,0000*5B'


def GetPoint():
    Tries=1
    if(cOMPort.ConnectedToGPSIndicator):
        time.sleep(.25) #Give 1 second for the GPS to stabilize at the current point
        for i in range(0, Tries):
            try:
                cOMPort.OpenPort.flushInput()  #Clear the input buffer before each get point attempt
                cOMPort.OpenPort.flushOutput()  #Clear the output buffer before each get point attempt
            except:
                pass
            Now=time.time()
            Future=time.time()
            while(Future - Now < .5):  #Readline for .5 second (will clear a buffer that flush appears to not clear)
                LogLine=gPS.GetCoordinate(cOMPort)  
                Future=time.time()
            if(gPS.CorrectedIndicator):
                break
            if(LogLine.startswith('No')):
                cOMPort.Connect()
    gPS.Save(ConnectionIndicator=cOMPort.ConnectedToGPSIndicator)
    

gPS=GPS()
cOMPort=COMPort()
cOMPort.Connect()



def ActivateWAASForHoluxM1200(cOMPort):
    cOMPort.OpenPort.flushOutput()
    cOMPort.OpenPort.flushInput()
    cOMPort.OpenPort.write('$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0*29\r\n') #GPGG
    time.sleep(.1)
    cOMPort.OpenPort.write('$PMTK160*35\r\n')
    time.sleep(.1)
    cOMPort.OpenPort.write('$PMTK319,0*25\r\n')
    time.sleep(.1)
    cOMPort.OpenPort.write('$PMTK313,1*2E\r\n')  #WAAS
    time.sleep(.1)
    cOMPort.OpenPort.write('$PMTK301,2*2E\r\n') #DGPS
    time.sleep(.1)

def UploadFTPFile(Server, UserName, Password, LocalFileName, RemoteFileName):
    FTPConnection=ftplib.FTP(Server, UserName, Password)
    FTPCommand = 'STOR '  + RemoteFileName
    LocalFile=open(LocalFileName)
    FTPConnection.storlines("Stor " + RemoteFileName, LocalFile)
    LocalFile.close()



while(True):
    ActivateWAASForHoluxM1200(cOMPort)  
    GetPoint()
    LocalFile="/home/robyg/Desktop/Locations.csv"
    try:
        UploadFTPFile(FTPServer, FTPUserName, FTPPassword, LocalFileName, FTPRemoteFileName)
    except: #normally will fail since the file cannot be created.
        pass
    time.sleep(2)    
        


    

